﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FjordagerGymnastikHvorBorMedlemmerne.FindAddressService;
using System.Xml;

namespace FjordagerGymnastikHvorBorMedlemmerne
{
    public class ConvertAdressToCoordinate
    {
        private KML kmlgenerator = null;
        public ConvertAdressToCoordinate()
        {
        }

        private Coordinate getCoordinateFromAdress(string streetname, string buildingnumber, string postalcode)
        {
            Coordinate coor = new Coordinate();
            FindAddressService.findaddressservice service = new FindAddressService.findaddressservice();
            FindAddressAccessRequestType faaReq = new FindAddressAccessRequestType();
            faaReq.AddressAccessSearch = new AddressAccessSearchType();
            faaReq.AddressAccessSearch.NamedStreetInput = streetname;
            faaReq.AddressAccessSearch.StreetBuildingIdentifierInput = buildingnumber;
            faaReq.AddressAccessSearch.MaxMatchLevel = "0"; // must be an exact match (streetname) 
            faaReq.AddressAccessSearch.StreetBuildingMaxJustification = "0"; // must be an exact match (streetnumber) 

            faaReq.PostalDistrictSearchCollection = new PostalDistrictSearchType[1];

            PostalDistrictSearchType pdst = new PostalDistrictSearchType();

            pdst.ItemElementName = ItemChoiceType.PostCodeIdentifierInput;
            pdst.Item = postalcode;
            pdst.NeighbourLevel = ("0");

            faaReq.PostalDistrictSearchCollection[0] = pdst;

            faaReq.RequestOptionCollection = new RequestOptionType[3];
            RequestOptionType rot1 = new RequestOptionType();
            rot1.name = "includeAddressAccess";
            rot1.Value = "true";
            faaReq.RequestOptionCollection[0] = rot1;

            RequestOptionType rot2 = new RequestOptionType();
            rot2.name = "includeAddressPostal";
            rot2.Value = "true";
            faaReq.RequestOptionCollection[1] = rot2;

            RequestOptionType rot3 = new RequestOptionType();
            rot3.name = "includeAddressPoint";
            rot3.Value = "true";
            faaReq.RequestOptionCollection[2] = rot3;
            SearchResultType[] result = service.FindAddressAccess(faaReq);

            if (result.Length == 1)
            {
                coor.X = (double)result[0].AddressPoint.GeographicPointLocation.GeographicCoordinateTuple.GeographicNorthingMeasure;
                coor.Y = (double)result[0].AddressPoint.GeographicPointLocation.GeographicCoordinateTuple.GeographicEastingMeasure;

            }
            //else
            //{
            //    for (int i = 0; i < result.Length; i++)
            //    {
            //        SearchResultType element = result[i];
            //        if (element.AddressPostal.PostCodeIdentifier.ToUpper() == postalcode.ToUpper())
            //        {
            //            if (element.AddressPostal.StreetName.ToUpper() == streetname.ToUpper())
            //            {
            //                if (element.AddressPostal.StreetBuildingIdentifier.ToUpper() == buildingnumber.ToUpper())
            //                {
            //                    coor.X = (double)element.AddressPoint.GeographicPointLocation.GeographicCoordinateTuple.GeographicNorthingMeasure;
            //                    coor.Y = (double)element.AddressPoint.GeographicPointLocation.GeographicCoordinateTuple.GeographicEastingMeasure;
            //                    break;
            //                }
            //            }
            //        }
            //    }
            //}

            return coor;
        }


        private List<Coordinate> ParseAdresses(System.ComponentModel.BackgroundWorker backgroundworker, string[] adresses, out List<string> errors)
        {
            errors = new List<string>();
            List<Coordinate> coordlist = new List<Coordinate>();
            for (int i = 0; i < adresses.Length; i++)
            {
                try
                {
                    string streetname;
                    string buildingnumber;
                    string postalcode;
                    string description;
                    ParseText(adresses[i], out streetname, out buildingnumber, out postalcode, out description);

                    Coordinate coor = getCoordinateFromAdress(streetname, buildingnumber, postalcode);
                    if (coor.X == 0.0 || coor.Y == 0.0)
                    {
                        errors.Add("Line " + i + " failed : " + adresses[i] + "," + "unable to locate the adress");
                        continue;
                    }
                    coor.Description = description;
                    //now transform to lat long
                    coor = Convert2LatLong(coor);

                    coordlist.Add(coor);
                    if (backgroundworker != null)
                    {
                        int procent = (100 * i / adresses.Length);
                        backgroundworker.ReportProgress(procent);
                    }
                }
                catch (Exception ex)
                {
                    errors.Add("Line " + i + " failed : " + adresses[i] +"," + ex.Message);
                    continue;
                }
                
            }
            
            return coordlist;
        }

        private void ParseText(string p, out string streetname, out string buildingnumber, out string postalcode, out string description)
        {
            char[] seperator = new char[1];
            seperator[0] = ';';
            string[] elementer = p.Split(seperator);
            // element 0 is the streetname and number
            streetname = "";
            buildingnumber = "";
            for (int i = 0; i < elementer[0].Length; i++)
            {
                // locate the first int
                int number;
                
                if (Int32.TryParse(elementer[0][i].ToString(), out number) == true)
                {
                    streetname = elementer[0].Substring(0, i - 1);
                    // remember to handle any "st.tv" or similar suffixes
                    string remaining = elementer[0].Substring(i);
                    int counter = 0;
                    for (int j = 0; j < remaining.Length; j++)
                    {
                        // in order to handle numbers like 37B I have to use a space as a delimeter
                        if (remaining[j].ToString() != " ")
                            counter++;
                        else
                            break;
                    }
                    // if what is left if the right hand side of the space is two chars or less it is most likely a part of the building number 
                    // and should be included
                    if ((remaining.Length - counter <= 2) && (remaining.Length >= counter +2))
                    {
                        buildingnumber = elementer[0].Substring(i, counter+2).Replace(" ", "");
                    }
                    else
                    {
                        buildingnumber = elementer[0].Substring(i, counter);
                    }

                    break;
                }
            }
            postalcode = elementer[1];
            description = elementer[2];
        }

        //internal List<Coordinate> Convert2LatLong(List<Coordinate> coordinatelist)
        //{
        //    List<Coordinate> positions = new List<Coordinate>();
        //    MercatorProjection converter = new MercatorProjection();
        //    foreach (Coordinate coor in coordinatelist)
        //    {
        //        double[] pos = converter.ETRS89ToWGS84((double)coor.X, (double)coor.Y);
        //        positions.Add(new Coordinate(pos[1], pos[0], coor.Describtion));
        //    }
        //    return positions;
        //}
        internal Coordinate Convert2LatLong(Coordinate coor)
        {
            MercatorProjection converter = new MercatorProjection();
                double[] pos = converter.ETRS89ToWGS84((double)coor.X, (double)coor.Y);
                return new Coordinate(pos[1], pos[0], coor.Description);
        }
        internal XmlDocument GenerateKML(List<Coordinate> Positions)
        {

            kmlgenerator = new KML();
            kmlgenerator.GenerateKml();
            
            foreach (Coordinate item in Positions)
            {
                kmlgenerator.AddPoint((float)item.Y, (float)item.X, item.Description);
            }
            
            XmlDocument xmldoc = kmlgenerator.getKML();
            return xmldoc;
        }

        internal void SaveKML(string path)
        {
            if (System.IO.File.Exists(path) == true)
                System.IO.File.WriteAllText(path, getKML());
            else
                throw new System.IO.FileNotFoundException("The output file was not found", path);
            
        }

        internal string getKML()
        {
            return kmlgenerator.doc.InnerXml.ToString();
        }
        public string GenerateKML(System.ComponentModel.BackgroundWorker backgroupworker, string filepath, out List<string> errors)
        {

            if (System.IO.File.Exists(filepath) == false)
            {
                System.IO.FileStream fs = System.IO.File.Create(filepath);
                if( fs == null)
                    throw new System.IO.FileNotFoundException("Inputfile not found", filepath);
                fs.Close();
            }
            string[] adresses = System.IO.File.ReadAllLines(filepath, System.Text.Encoding.GetEncoding("iso-8859-1"));
            return GenerateKML(backgroupworker, adresses, out errors);
        }
        private string GenerateKML(System.ComponentModel.BackgroundWorker backgroundworker, string[] adresses, out List<string> errors)
        {
            List<Coordinate> coordinatelist = ParseAdresses(backgroundworker, adresses, out errors);
            GenerateKML(coordinatelist);
            return getKML();
        }

        
    }
}
